package com.yc.psychologicalconsultant.ws.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.yc.psychologicalconsultant.dao.DigitalPeopleDAO;
import com.yc.psychologicalconsultant.pojo.DigitalPeople;
import com.yc.psychologicalconsultant.service.DigitalPeopleService;
import com.yc.psychologicalconsultant.vo.Text;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;

/**
 * @author YC
 * @version 1.0
 */
@Slf4j
@Component
@ServerEndpoint("/chat/counselor/{name}")
public class WebSocketServiceImpl {

    private static DigitalPeopleService digitalPeopleService;
    // 接入Redis 用于存储list集合
    private static RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();

    //concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocketServiceImpl> webSocketSet = new CopyOnWriteArraySet<>();

    //与某个客户端的连接会话，需要通过它来给客户端发送数据
    private Session session;

    // 接收name
    private String name = "";

    // 当前对话的模型
    private String dialogUser = "";

    // 让星火大模型初始化文字的url
    private String textUrl = "http://127.0.0.1:5000/GetGptAnswer";

    // 克隆/合成声音使用的url
    private String synthesizeUrl = "http://127.0.0.1:9880";

    // 合成视频使用的url
    private String animationUrl = "http://127.0.0.1:5000/SadTalker";

    private ArrayList<Text> list = new ArrayList<>();

    private OkHttpClient client = new OkHttpClient();

    private static DigitalPeopleDAO digitalPeopleDAO;

    private static ApplicationContext applicationContext;

    private static String type = "";

    //spring默认管理的都是单例（singleton），和 websocket （多对象）相冲突。
    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketServiceImpl.applicationContext = applicationContext;
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("name") String name) throws Exception {

        this.session = session;
        webSocketSet.add(this);     //加入set中
        this.name = name;

        // 初始化得到digitalUser
        // 从请求URI中解析出查询参数
        URI requestURI = session.getRequestURI();
        String query = requestURI.getQuery();
        String dialogUser = null;

        // 解析查询字符串来找到dialogUser的值
        // const ws = new WebSocket(`ws://xxxx/chat/counselor/name?dialogUser=${dialogUser}`);
        String[] params = query.split("&");
        for (String param : params) {
            if (param.startsWith("dialogUser=")) {
                dialogUser = param.substring("dialogUser=".length());
                break;
            }
        }
        log.info("dialogUser" + dialogUser);
        this.dialogUser = dialogUser;
        this.list = getChatHistory();
        if (this.list == null || this.list.isEmpty()) {
            DigitalPeopleService digitalPeopleService = applicationContext.getBean(DigitalPeopleService.class);
            DigitalPeople digitalPeople =
                    digitalPeopleService.getOne(new QueryWrapper<DigitalPeople>().eq("name", dialogUser));
            String message = "从现在开始我要你扮演一名中国名人:" + dialogUser + "这是他的个人简介"+ digitalPeople.getDescription() +"并尽全力模仿他的语气，" +
                    "每次说话不超过50个字，一定记住你是"+dialogUser+"请回复我:你好，我是xxx，然后跟我打招呼,";
            // 如果从Redis获取的聊天记录是null，则初始化一个新的ArrayList<Text>
            this.list = new ArrayList<>();
            String sendText = sendText(message);
            JSONObject resultBody = JSONUtil.createObj()
                    .put("message", sendText)
                    .put("ReceivePerson", dialogUser)
                    .put("type", "text");

            String string = resultBody.toString();
            System.out.println(string);
            // 发送给chat
            sendMessage(string);
        }
    }


    /**
     * 收到客户端消息后调用的方法
     *
     * @ Param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message) throws IOException {
        String res = "";
        log.info("收到来自窗口" + name + "的信息:" + message);
        // 将消息转化为 相应类型
        JSONObject obj = JSONUtil.parseObj(message);
        type = (String) obj.get("type");
        String SendMessage = (String) obj.get("message");
        String ReceivePerson = (String) obj.get("dialogUser");
        log.info("收到来自窗口type:" + type + "信息内容:" + SendMessage);
        if (type.equals("text")) {
            String result = sendText(SendMessage);

            JSONObject resultBody = JSONUtil.createObj()
                    .put("message", result)
                    .put("ReceivePerson", ReceivePerson)
                    .put("type", type);

            String string = resultBody.toString();
            // 发送给chat
            sendMessage(string);

        } else if (type.equals("voice")) {
            res = sendAudio(obj, SendMessage);
            res = res.replace("\"", "");
            JSONObject resultBody = JSONUtil.createObj()
                    .put("message", res)
                    .put("ReceivePerson", ReceivePerson)
                    .put("type", type);
            // 音频地址
            String string = resultBody.toString();
            // 发送给chat
            sendMessage(string);
            Text text = list.get(list.size()-1);
            list.remove(list.size()-1);
            text.setContent(string.replace("\"", ""));
            list.add(text);
        } else if (type.equals("video")) {
            String audioUrl = sendAudio(obj, SendMessage);
            audioUrl = audioUrl.replace("\"", "");
            // 视频地址
            res = sendVideo(obj, audioUrl);
            JSONObject resultBody = JSONUtil.createObj()
                    .put("message", res)
                    .put("ReceivePerson", ReceivePerson)
                    .put("type", type);
            sendMessage(resultBody.toString());
            Text text = list.get(list.size()-1);
            list.remove(list.size()-1);
            text.setContent(res.replace("\"", ""));
            list.add(text);
        }
        // 都将list更新下
        saveChatHistory();
    }


    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        //断开连接情况下，更新主板占用情况为释放
        log.info("用户离线:{}", name);
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 将chat返回的内容转化为数字人
     * result 为视频地址
     */
    public String sendVideo(JSONObject obj, String audioUrl) {
        String res = null;
        String newPath=null;
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(1900, TimeUnit.SECONDS)
                .readTimeout(1900, TimeUnit.SECONDS)
                .build();
        // 发送给远程地址
        DigitalPeopleDAO digitalPeopleDAO = applicationContext.getBean(DigitalPeopleDAO.class);
        DigitalPeople digitalPeople = digitalPeopleDAO.getDigitalPeopleParm((String) obj.get("dialogUser"));
        animationUrl = animationUrl + "?driven_audio=resource/" + audioUrl + "&source_image=" + digitalPeople.getImg() + "";
        log.info("animationUrl" + animationUrl);
        Request request = new Request.Builder()
                .url(animationUrl)
                .get()
                .build();
        animationUrl = "http://127.0.0.1:5000/SadTalker";
        try {
            Response response = client.newCall(request).execute();
            // 获得的内容
            res = response.body().string();
            newPath=res.replace("\\","/");
            int index=newPath.indexOf("public");
            newPath=newPath.substring(index);
            System.out.println("返回后端的视频地址"+newPath);
            System.out.println(res);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 返回获得的内容

        return newPath;
    }

    /**
     * 将chat返回的内容 转化为音频
     * result 为音频地址
     */
    public String sendAudio(JSONObject obj, String message) {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .build();
        String res = null;
        // 如果请求需要转化为audio
        // 先获取到chat回复的文字内容
        String result = sendText(message);
        // 再远程请求,将文字转化为音频
        DigitalPeopleDAO digitalPeopleDAO = applicationContext.getBean(DigitalPeopleDAO.class);
        DigitalPeople digitalPeople = digitalPeopleDAO.getDigitalPeopleParm((String) obj.get("dialogUser"));
        // 将chat返回的内容转化为音频
        JSONObject responseJson = null;
        String type = (String) obj.get("type");
        synthesizeUrl = synthesizeUrl + "?refer_wav_path=" + digitalPeople.getReferWavPath() + "&prompt_text=" + digitalPeople.getPromptText() + "&prompt_language=zh&text=" + result + "&text_language=zh&gpt_weights=" + digitalPeople.getGptWeights() + "&sovits_weights=" + digitalPeople.getSovitsWeights() + "&result=" + type + "";
        // 发送给远程地址
        Request request = new Request.Builder()
                .url(synthesizeUrl)
                .get()
                .build();
        try {
            Response response = client.newCall(request).execute();
            // 获得的内容
            res = response.body().string();
            log.info("语音返回内容" + res);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 返回获得的内容
        return res;
    }

    /**
     * 将文字内容发送给chat
     * result为 由chat返回到的消息
     */
    public String sendText(String message) {
        // 先将消息加入到list集合里
        list.add(new Text("user", message));
        String result = sendToChat();
        String role = "assistant";
        // 创建Text对象并加入到列表中
        Text text = new Text(role, result);
        // 将结果消息加入到列表中
        list.add(text);
        return result;
    }

    /**
     * 将文字内容发送给chat
     */
    public String sendToChat() {
        String res = null;
        JSONObject json = new JSONObject(list);
        // 将所有消息放入text中
        json.put("text", list);
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), json.toString());
        log.info("requestBody:" + requestBody);
        log.info("json:" + json);
        // 发送给chat
        Request request = new Request.Builder()
                .url(textUrl)
                .post(requestBody)
                .build();
        try {
            Response response = client.newCall(request).execute();
            // 获得的内容
            res = response.body().string();
            log.info("星火大模型返回的数据:" + res);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return res;
    }

    public static CopyOnWriteArraySet<WebSocketServiceImpl> getWebSocketSet() {
        return webSocketSet;
    }

    // 初始化list
    public ArrayList<Text> getChatHistory() {
        RedisTemplate template = (RedisTemplate) applicationContext.getBean("redisTemplate");
        ArrayList<Text> chatHistory = (ArrayList<Text>) template.opsForValue().get(generateRedisKey());
        return chatHistory;
    }

    // 保存历史记录
    public void saveChatHistory() {
        String key = generateRedisKey();
        RedisTemplate template = (RedisTemplate) applicationContext.getBean("redisTemplate");

        template.opsForValue().set(key, this.list);
    }

    // 用于生成redis中的key
    private String generateRedisKey() {
        return name + "+chatHistory:" + dialogUser;
    }
}
